home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac Power 1997 December
/
MACPOWER-1997-12.ISO.7z
/
MACPOWER-1997-12.ISO
/
AMUG
/
PROGRAMMING
/
Raven 1.2.sit
/
Raven 1.2
/
Source
/
Foundation
/
Common
/
ZMemoryHeap.cpp
< prev
next >
Wrap
Text File
|
1997-09-04
|
20KB
|
847 lines
/*
* File: ZMemoryHeap.cpp
* Summary: A class that uses a TAllocator and zero or more TFixedAllocators
* to allocate blocks of memory.
* Written by: Jesse Jones
*
* Copyright ゥ 1997 Jesse Jones.
* For conditions of distribution and use, see copyright notice in ZTypes.h
*
* Change History (most recent first):
*
* <7> 8/15/97 JDJ Block count functions are defined if !RELEASE (instead
* of if DEBUG).
* <6> 5/02/97 JDJ Fixed an ASSERT in AddAllocator.
* <5> 4/13/97 JDJ Includes Gestalt.h
* <4> 3/05/97 JDJ Rewrote FSSpecOpen.
* <3> 2/28/97 JDJ DumpLeaks tells people to turn off virtual memory.
* <2> 2/25/97 JDJ Deallocate validates the block before zapping it.
* <1> 1/29/97 JDJ Created
*/
#include <ZMemoryHeap.h>
#include <Errors.h>
#include <Gestalt.h>
#include <Limits.h>
#include <Processes.h>
#include <Stdio.h>
#include <TextUtils.h>
#include <ZAllocator.h>
#include <ZDebug.h>
#include <ZExceptions.h>
#include <ZFixedAllocator.h>
#include <ZMemUtils.h>
#include <ZStackCrawl.h>
#include <ZStringUtils.h>
#if !RELEASE
#include <ZDialogUtils.h>
#endif
//-----------------------------------
// Constants
//
const long kStackDepth = 8; // use an even number to keep data aligned on an 8-byte boundary
const long kDebugHeaderLen = kStackDepth*sizeof(StackFrameID) + 2*sizeof(long);
const long kDebugTrailerLen = sizeof(long);
const long kDebugOverhead = kDebugHeaderLen + kDebugTrailerLen;
// ===================================================================================
// struct SMemoryBlock
// ===================================================================================
#if DEBUG
struct SMemoryBlock {
StackFrameID crawl[kStackDepth]; // stack crawl (first is nil if no stack crawl)
ulong bytes; // exact size of user data
long marker; // 0xF1F1F1F1
Byte data[4]; // variable length data
// Byte marker2; // four or more 0xF2 bytes
};
#else
struct SMemoryBlock {
Byte data[4]; // variable length data
};
#endif
// ===================================================================================
// Internal Functions
// ===================================================================================
//---------------------------------------------------------------
//
// GetBlock (void*)
//
//---------------------------------------------------------------
inline SMemoryBlock* GetBlock(void* ptr)
{
#if DEBUG
SMemoryBlock* block = reinterpret_cast<SMemoryBlock*>((char*) ptr - kDebugHeaderLen);
#else
SMemoryBlock* block = reinterpret_cast<SMemoryBlock*>(ptr);
#endif
return block;
}
//---------------------------------------------------------------
//
// GetBlock (const void*)
//
//---------------------------------------------------------------
inline const SMemoryBlock* GetBlock(const void* ptr)
{
#if DEBUG
const SMemoryBlock* block = reinterpret_cast<const SMemoryBlock*>((char*) ptr - kDebugHeaderLen);
#else
const SMemoryBlock* block = reinterpret_cast<const SMemoryBlock*>(ptr);
#endif
return block;
}
//---------------------------------------------------------------
//
// VirtualMemIsOn
//
//---------------------------------------------------------------
static bool VirtualMemIsOn()
{
long result;
bool on = Gestalt(gestaltVMAttr, &result) == noErr && (result & 1) == 1;
return on;
}
//---------------------------------------------------------------
//
// FSSpecOpen
//
//---------------------------------------------------------------
#if DEBUG
static FILE* FSSpecOpen(const FSSpec& spec, const char* mode)
{
FILE* file = nil;
short oldVolume;
long oldDir;
OSErr err = HGetVol(nil, &oldVolume, &oldDir);
if (err == noErr) {
err = HSetVol(0, spec.vRefNum, spec.parID);
if (err == noErr) {
char fName[256];
ulong len = spec.name[0];
BlockMoveData(spec.name + 1, fName, len);
fName[len] = '¥0';
file = fopen(fName, mode);
}
HSetVol(nil, oldVolume, oldDir);
}
return file;
}
#endif
//---------------------------------------------------------------
//
// ValidateBlock
//
//---------------------------------------------------------------
#if DEBUG
static void ValidateBlock(const void* ptr, long size, void* refCon)
{
#pragma unused(refCon)
const SMemoryBlock* block = reinterpret_cast<const SMemoryBlock*>(ptr);
ASSERT(block != nil);
if (block->crawl[0] == nil)
for (ulong index = 1; index < kStackDepth; index++)
ASSERT(block->crawl[index] == nil);
ASSERT(block->bytes <= size - kDebugOverhead);
ASSERT(block->marker == 0xF1F1F1F1);
Byte* trailer = reinterpret_cast<Byte*>((char*) block + kDebugHeaderLen + block->bytes);
Byte* end = reinterpret_cast<Byte*>((char*) block + size);
ASSERT(trailer < end);
while (trailer < end)
ASSERT(*trailer++ == 0xF2);
}
#endif
//---------------------------------------------------------------
//
// DumpLeaks
//
//---------------------------------------------------------------
#if DEBUG
static void DumpLeaks(const void* ptr, long size, void* refCon)
{
ASSERT(refCon != nil);
ValidateBlock(ptr, (long) size, refCon);
const SMemoryBlock* block = reinterpret_cast<const SMemoryBlock*>(ptr);
FILE* file = reinterpret_cast<FILE*>(refCon);
if (block->crawl[0] != nil) {
fprintf(file, "Block size = %d¥n", block->bytes);
for (ulong index = kStackDepth - 1; index != ULONG_MAX; index--) {
StackFrameID id = block->crawl[index];
if (id != nil) { // stack may be smaller than kStackDepth
SStackFrame frame = TStackCrawl::GetFrame(id);
fprintf(file, "%s + 0x%lX¥n", frame.name.c_str(), frame.offset);
}
}
fprintf(file, "¥n");
}
}
#endif
#pragma mark -
// ===================================================================================
// class TMemoryHeap
// ===================================================================================
//---------------------------------------------------------------
//
// TMemoryHeap::~TMemoryHeap
//
//---------------------------------------------------------------
TMemoryHeap::~TMemoryHeap()
{
for (ulong index = 0; index < 256; index++)
delete mFixedAllocators[index];
delete mAllocator;
}
//---------------------------------------------------------------
//
// TMemoryHeap::TMemoryHeap
//
//---------------------------------------------------------------
TMemoryHeap::TMemoryHeap(TAllocator* takeAllocator)
{
ASSERT(takeAllocator != nil);
ASSERT(kDebugOverhead % 4 == 0); // so fixed allocator sizes stay on 4-byte boundaries
ASSERT(kDebugOverhead == sizeof(SMemoryBlock));
ASSERT(offsetof(SMemoryBlock, data) == kDebugHeaderLen);
ASSERT(offsetof(SMemoryBlock, data) % 8 == 0);
mAllocator = takeAllocator;
mBlockCount = 0;
mLeakCount = 0;
mHighwater = 0;
mCurrentBytes = 0;
mDebugOverhead = 0;
for (ulong index = 0; index < 256; index++)
mFixedAllocators[index] = nil;
#if DEBUG
mZap = true;
mLeakChecking = 1;
#endif
#if !RELEASE
for (ulong index = 0; index < 256; index++) {
mTotalBlockCounts[index] = 0;
mCurrentBlockCounts[index] = 0;
}
#endif
}
//---------------------------------------------------------------
//
// TMemoryHeap::AddAllocator
//
//---------------------------------------------------------------
void TMemoryHeap::AddAllocator(ulong size, ulong maxBlocks)
{
ASSERT(size > 0);
ASSERT(size < 256);
ASSERT(size % 4 == 0);
ASSERT(maxBlocks > 0);
#if DEBUG
size += kDebugOverhead;
ASSERT(mFixedAllocators[size] == nil);
long temp = mLeakChecking;
try {
mLeakChecking = 0;
ASSERT(!this->IsLeakChecking());
if (size < 256)
mFixedAllocators[size] = new TFixedAllocator(size, maxBlocks);
mLeakChecking = temp;
} catch (...) {
mLeakChecking = temp;
throw;
}
#else
if (size < 256)
mFixedAllocators[size] = new TFixedAllocator(size, maxBlocks);
#endif
}
//---------------------------------------------------------------
//
// TMemoryHeap::GetAllocator
//
//---------------------------------------------------------------
const TFixedAllocator* TMemoryHeap::GetAllocator(ulong size) const
{
ASSERT(size < 256);
const TFixedAllocator* alloc = nil;
#if DEBUG
size += kDebugOverhead;
#endif
if (size < 256)
alloc = mFixedAllocators[size];
return alloc;
}
//---------------------------------------------------------------
//
// TMemoryHeap::GetTotalBlockCount
//
//---------------------------------------------------------------
#if !RELEASE
ulong TMemoryHeap::GetTotalBlockCount(ulong size) const
{
ASSERT(size < 256);
ulong count = 0;
#if DEBUG
size += kDebugOverhead;
#endif
if (size < 256)
count = mTotalBlockCounts[size];
return count;
}
#endif
//---------------------------------------------------------------
//
// TMemoryHeap::GetCurrentBlockCount
//
//---------------------------------------------------------------
#if !RELEASE
ulong TMemoryHeap::GetCurrentBlockCount(ulong size) const
{
ASSERT(size < 256);
ulong count = 0;
#if DEBUG
size += kDebugOverhead;
#endif
if (size < 256)
count = mCurrentBlockCounts[size];
return count;
}
#endif
//---------------------------------------------------------------
//
// TMemoryHeap::DumpCommonBlocks
//
//---------------------------------------------------------------
#if !RELEASE
struct SBlockCount {
ulong size;
ulong current;
ulong total;
bool operator==(const SBlockCount& rhs) const {return total == rhs.total;}
bool operator<(const SBlockCount& rhs) const {return total < rhs.total;}
};
#if !SGI_STL
__MSL_FIX_ITERATORS__(SBlockCount);
#endif
void TMemoryHeap::DumpCommonBlocks()
{
char buffer[4096];
long offset = 0;
offset += sprintf(buffer + offset, "Bytes Current Total¥r");
SBlockCount blocks[256];
for (ulong index = 0; index < 256; index++) {
blocks[index].size = index;
blocks[index].current = this->GetCurrentBlockCount(index);
blocks[index].total = this->GetTotalBlockCount(index);
}
sort(blocks, blocks + 256);
for (long index = 255; index >= 255 - 3; index--)
offset += sprintf(buffer + offset, " %3d %7d %7d¥r", blocks[index].size, blocks[index].current, blocks[index].total);
DoStop("Number of blocks allocated for sizes under 256", buffer);
}
#endif
//---------------------------------------------------------------
//
// TMemoryHeap::DumpAllocatorCapacities
//
//---------------------------------------------------------------
#if !RELEASE
void TMemoryHeap::DumpAllocatorCapacities()
{
char buffer[4096];
long offset = 0;
offset += sprintf(buffer + offset, "Bytes Current Max¥r");
for (ulong size = 1; size < 256; size++) {
const TFixedAllocator* alloc = this->GetAllocator(size);
if (alloc != nil)
offset += sprintf(buffer + offset, " %3d %4d%% %4d%%¥r", size, alloc->GetCurrentPercent(), alloc->GetMaxPercent());
}
DoStop("Fixed allocator capacities", buffer);
}
#endif
//---------------------------------------------------------------
//
// TMemoryHeap::Allocate
//
//---------------------------------------------------------------
void* TMemoryHeap::Allocate(ulong actualBytes)
{
ASSERT(actualBytes < 16*1024L*1024L);
SMemoryBlock* block = nil;
ulong bytes = (actualBytes + 3) & ~3; // round bytes up to next 4-byte boundary
#if DEBUG
bytes += kDebugOverhead;
#endif
if (bytes < 256 && mFixedAllocators[bytes] != nil)
block = reinterpret_cast<SMemoryBlock*>(mFixedAllocators[bytes]->Allocate());
if (block == nil)
block = reinterpret_cast<SMemoryBlock*>(mAllocator->Allocate(bytes));
if (block != nil) {
mBlockCount++;
ulong size = this->GetTotalBlockSize(block->data);
mCurrentBytes += size;
#if DEBUG
this->AddDebugInfo(block, actualBytes, this->GetBlockSize(block->data) + kDebugOverhead); // bytes may be off by a few
if (this->IsLeakChecked(block->data))
mLeakCount++;
mDebugOverhead += kDebugOverhead;
#if !__profile__
if (mZap)
SetMemory(block->data, kNewFill, block->bytes);
#endif
#endif
#if !RELEASE
if (bytes < 256) {
mTotalBlockCounts[bytes] += 1;
mCurrentBlockCounts[bytes] += 1;
}
#endif
if (mCurrentBytes > mHighwater)
mHighwater = mCurrentBytes;
}
return block != nil ? block->data : nil;
}
//---------------------------------------------------------------
//
// TMemoryHeap::Deallocate
//
//---------------------------------------------------------------
void TMemoryHeap::Deallocate(void* ptr)
{
if (ptr != nil) {
SMemoryBlock* block = GetBlock(ptr);
ulong bytes = this->GetBlockSize(block->data);
#if DEBUG
mDebugOverhead -= kDebugOverhead;
if (this->IsLeakChecked(block->data))
mLeakCount--;
#if !__profile__
this->ValidateBlock(block->data);
if (mZap)
SetMemory(block->data, kFreeFill, block->bytes);
#endif
bytes += kDebugOverhead;
#endif
#if DEBUG
// Allocated bytes may be slightly larger than requested bytes.
ulong requestedBytes = ((block->bytes + 3) & ~3) + kDebugOverhead;
if (requestedBytes < 256)
mCurrentBlockCounts[requestedBytes] -= 1;
#endif
mBlockCount--;
ulong size = this->GetTotalBlockSize(block->data);
if (bytes < 256 && mFixedAllocators[bytes] != nil && mFixedAllocators[bytes]->HasBlock(block))
mFixedAllocators[bytes]->Deallocate(block);
else
mAllocator->Deallocate(block);
mCurrentBytes -= size;
}
}
//---------------------------------------------------------------
//
// TMemoryHeap::GetHeapSize
//
//---------------------------------------------------------------
ulong TMemoryHeap::GetHeapSize() const
{
ulong size = mAllocator->GetHeapSize();
for (ulong index = 4; index < 256; index += 4)
if (mFixedAllocators[index] != nil)
size += mFixedAllocators[index]->GetHeapSize();
return size;
}
//---------------------------------------------------------------
//
// TMemoryHeap::GetPoolCount
//
//---------------------------------------------------------------
ulong TMemoryHeap::GetPoolCount() const
{
ulong count = mAllocator->GetPoolCount();
return count;
}
//---------------------------------------------------------------
//
// TMemoryHeap::GetBlockSize
//
//---------------------------------------------------------------
ulong TMemoryHeap::GetBlockSize(const void* ptr) const
{
ASSERT(ptr != nil);
ulong size = 0;
const SMemoryBlock* block = GetBlock(ptr);
for (ulong bytes = 4; bytes < 256 && size == 0; bytes += 4) { // should be fast since there should only be a few fixed allocators
if (mFixedAllocators[bytes] != nil && mFixedAllocators[bytes]->HasBlock(block))
size = bytes;
}
if (size == 0)
size = mAllocator->GetBlockSize(block);
#if DEBUG
size -= kDebugOverhead;
#endif
return size;
}
//---------------------------------------------------------------
//
// TMemoryHeap::GetTotalBlockSize
//
//---------------------------------------------------------------
ulong TMemoryHeap::GetTotalBlockSize(const void* ptr) const
{
ASSERT(ptr != nil);
ulong size = 0;
const SMemoryBlock* block = GetBlock(ptr);
for (ulong bytes = 4; bytes < 256 && size == 0; bytes += 4) {
if (mFixedAllocators[bytes] != nil && mFixedAllocators[bytes]->HasBlock(block))
size = bytes;
}
if (size == 0)
size = mAllocator->GetTotalBlockSize(block);
return size;
}
//---------------------------------------------------------------
//
// TMemoryHeap::ValidateBlock
//
//---------------------------------------------------------------
#if DEBUG
void TMemoryHeap::ValidateBlock(const void* ptr) const
{
ASSERT(ptr != nil);
const SMemoryBlock* block = GetBlock(ptr);
ulong size = 0;
for (ulong bytes = 4; bytes < 256 && size == 0; bytes += 4)
if (mFixedAllocators[bytes] != nil && mFixedAllocators[bytes]->HasBlock(block))
size = bytes;
if (size == 0) {
mAllocator->ValidateBlock(block);
size = mAllocator->GetBlockSize(block);
}
::ValidateBlock(block, (long) size, nil);
}
#endif
//---------------------------------------------------------------
//
// TMemoryHeap::ValidateHeap
//
//---------------------------------------------------------------
#if DEBUG
void TMemoryHeap::ValidateHeap() const
{
for (ulong bytes = 4; bytes < 256; bytes += 4)
if (mFixedAllocators[bytes] != nil)
mFixedAllocators[bytes]->ValidateHeap(::ValidateBlock);
mAllocator->ValidateHeap(::ValidateBlock);
}
#endif
//---------------------------------------------------------------
//
// TMemoryHeap::IsLeakChecked
//
//---------------------------------------------------------------
#if DEBUG
bool TMemoryHeap::IsLeakChecked(const void* ptr) const
{
ASSERT(ptr != nil);
const SMemoryBlock* block = GetBlock(ptr);
return block->crawl[0] != nil;
}
#endif
//---------------------------------------------------------------
//
// TMemoryHeap::AddDebugInfo
//
//---------------------------------------------------------------
#if DEBUG
void TMemoryHeap::AddDebugInfo(SMemoryBlock* block, ulong actualBytes, ulong size)
{
ASSERT(block != nil);
ASSERT(size >= sizeof(SMemoryBlock));
block->bytes = actualBytes;
block->marker = 0xF1F1F1F1;
ulong index;
for (index = 0; index < kStackDepth; index++)
block->crawl[index] = nil;
Byte* trailer = reinterpret_cast<Byte*>((char*) block + kDebugHeaderLen + actualBytes);
Byte* end = reinterpret_cast<Byte*>((char*) block + size);
while (trailer < end)
*trailer++ = 0xF2;
#if !__profile__
if (this->IsLeakChecking()) {
TStackCrawl crawl(4, kStackDepth);
ulong count = crawl.GetNumFrames(); // stack depth may be smaller than kStackDepth
for (index = 0; index < count; index++)
block->crawl[index] = crawl.GetID(index);
}
#endif
}
#endif
//---------------------------------------------------------------
//
// TMemoryHeap::GetLeaksLog
//
//---------------------------------------------------------------
#if DEBUG
FSSpec TMemoryHeap::GetLeaksLog() const
{
FSSpec spec;
ProcessSerialNumber psn;
OSErr err = GetCurrentProcess(&psn);
ThrowIfOSErr(err);
ProcessInfoRec info;
info.processInfoLength = sizeof(info);
info.processName = 0;
info.processAppSpec = &spec;
err = GetProcessInformation(&psn, &info);
ThrowIfOSErr(err);
unsigned char* name = "¥pleaks.log";
BlockMoveData(name, spec.name, name[0] + 1UL);
return spec;
}
#endif
//---------------------------------------------------------------
//
// TMemoryHeap::DumpLeaks
//
//---------------------------------------------------------------
#if DEBUG
void TMemoryHeap::DumpLeaks() const
{
FILE* file = nil;
try {
FSSpec spec = this->GetLeaksLog();
file = FSSpecOpen(spec, "w");
if (file == nil)
ThrowOSErr(openErr);
if (mLeakCount == 0)
fprintf(file, "No memory leaks.¥n");
else {
if (VirtualMemIsOn()) // ・・・ハStack crawl code doesn't work quite right with virtual memory
fprintf(file, "Turn off virtual memory to see the symbol names in the stack crawl.¥n¥n");
if (mLeakCount == 1)
fprintf(file, "You have one leak:¥n¥n");
else
fprintf(file, "You have %d leaks:¥n¥n", mLeakCount);
for (ulong bytes = 4; bytes < 256; bytes += 4)
if (mFixedAllocators[bytes] != nil)
mFixedAllocators[bytes]->ValidateHeap(::DumpLeaks, file);
mAllocator->ValidateHeap(::DumpLeaks, file);
}
} catch (const TSystemException& e) {
DEBUGSTR("Couldn't dump the leaks.log because of an %d error", e.mError);
} catch (...) {
DEBUGSTR("Couldn't dump the leaks.log because of an unknown error");
}
fclose(file);
}
#endif
//---------------------------------------------------------------
//
// TMemoryHeap::operator new [static]
//
//---------------------------------------------------------------
void* TMemoryHeap::operator new(size_t size)
{
void* ptr = NewPtr(size);
ThrowIfNil(ptr);
return ptr;
}
//---------------------------------------------------------------
//
// TMemoryHeap::operator delete [static]
//
//---------------------------------------------------------------
void TMemoryHeap::operator delete(void* ptr)
{
if (ptr != nil)
DisposePtr((Ptr) ptr);
}